ShallowRiver

php magic function

字数统计: 447阅读时长: 2 min
2019/11/06 Share

unserialize的漏洞通常发生在magic function上,也就是我们常说的魔法函数。
如果一个类定义了__wakeup()和__destruct(),则该类的实例被反序列化时,会自动调用__wakeup(),生命周期结束也就是被销毁的时候,就调用__destruct()/
下面给出一个简单的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
class Demo
{

public $data;

public function __construct($data)
{
$this->data = $data;
echo "construct<br />";
}

public function __wakeup()
{
echo "wake up<br />";
}

public function __destruct()
{
echo "Data's value is $this->data. <br />";
echo "destruct<br />";
}
}
var_dump(serialize(new Demo("raw value")));
?>

可以看到输出结果为:
construct
Data’s value is raw value.
destruct
string(42) “O:4:”Demo”:1:{s:4:”data”;s:9:”raw value”;}”
[Finished in 0.5s]
由于我们并没有对实例进行反序列化因此不会调用__wakeup()函数
我们在下面加入
unserialize(‘O:4:”Demo”:1:{s:4:”data”;s:15:”malicious value”;}’);
则输出结果调用
construct
Data’s value is raw value.
destruct
string(42) “O:4:”Demo”:1:{s:4:”data”;s:9:”raw value”;}”

wake up
Data’s value is malicious value.
destruct
[Finished in 0.1s]
可以看到在对我们输入的内容进行反序列化时,data的值被我们传入的malicious value覆盖了。

由于反序列化时会触发__wakeup(),那么我们能否在__wakeup()中加入判断来避免我们构造的data的值在unserialize时覆盖掉原有的值呢
例如下面的例子

1
2
3
4
5
public function __wakeup()
{
if($this->data != 'raw value') $this->data = 'raw value';
echo "wake up<br />";
}

但是这样其实还可以是绕过的,在PHP5<5.6.25,PHP7<7.0.10的版本都存在wakeup的楼龙。当反序列化中object的个数和之前的个数不等时,wakeup就会被绕过,于是使用下面的payload
unserialize('O:6:"HITCON":2:{s:4:"data";s:15:"malicious value";}');

CATALOG